# Copyright 2008 Kaspars Dancis
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.



import logging, base64
from datetime import datetime

from google.appengine.api import datastore, datastore_admin, datastore_types, datastore_errors

from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.api import memcache

from rocket.consts import *
from rocket.config import SECRET_KEY

from rocket import utils

import model

class Rocket(webapp.RequestHandler):
    def get(self):    
        path = self.request.path.split("/")
                
        self.response.headers['Content-Type'] = 'text/xml'

        if self.request.get("secret_key") !=  SECRET_KEY:
            self.response.out.write(u'<error>Unauthorized</error>\n')
            return 
            
        if len(path) < 3 or path[2] == '': 
            self.response.out.write(u'<error>Please specify an entity kind</error>\n')
            return 
        
        kind = path[2]
    
        self.response.out.write(u'<?xml version="1.0" encoding="UTF-8"?>\n')    
        self.response.out.write(u'<updates>\n')
        
        query = datastore.Query(kind)
        
        timestamp_field = self.request.get("timestamp")
        if not timestamp_field:
            timestamp_field = DEFAULT_TIMESTAMP_FIELD        
       
        batch_size = self.request.get("count")
        if not batch_size:
            batch_size = DEFAULT_BATCH_SIZE
        else:
            batch_size = int(batch_size)
            
        f = self.request.get("from") 
        if f: 
            query['%s >' % timestamp_field] = utils.from_iso(f)
    
        query.Order(timestamp_field)
            
        entities = query.Get(batch_size, 0)
        
        for entity in entities:
            self.response.out.write(u'    <%s key="%s">\n' % (kind, ae_to_rocket(TYPE_KEY, entity.key())))
            
            for field, value in entity.items():
                #print "sending field - " + field
                #logger.debug("sending field - " + field)
                if isinstance(value, list):
                    if len(value) > 0 and value[0] != None:
                        field_type = get_type(value[0])
                        self.response.out.write(u'        <%s type="%s" list="true">\n' % (field, field_type))
                        for item in value:
                            self.response.out.write(u"            <item>%s</item>\n" % ae_to_rocket(field_type, item))                    
                        self.response.out.write(u'</%s>\n' % field)
                else:
                    if value != None:  
                        if field == timestamp_field:
                            field_type = TYPE_TIMESTAMP
                        else:
                            field_type = get_type(value)
                        
                        self.response.out.write(u'        <%s type="%s">%s</%s>\n' % (field, field_type, ae_to_rocket(field_type, value), field))                
    
            self.response.out.write(u'    </%s>\n' % kind)
                
        self.response.out.write(u'</updates>')
        
        
    def post(self):
        path = self.request.path.split("/")
        
        self.response.headers['Content-Type'] = 'text/plain'

        if len(path) < 3 or path[2] == '': 
            self.response.out.write(u'Please specify an entity kind\n')
            return 
        
        kind = path[2]
        
        entity = None
        clear_cache = False
        
        key_name_or_id = self.request.get(TYPE_KEY)
        self.response.out.write(u'key_name_or_id : ' + key_name_or_id)
        #print "kind: " + str(kind)
        #print "key: " + str(key_name_or_id)
        new_key = None
        
        if key_name_or_id:
            if key_name_or_id[0] in "0123456789":
                
                #print "ID?"
                try: 
                    #entity = getattr(model, kind)(datastore.Get(key))
                    #ogging.debug("key_name_or_id : " + key_name_or_id)
                    
                    #entity = getattr(model, kind).all().filter('ID =',key_name_or_id).fetch(1)[0]
                    #self.response.out.write(u'key from path : ' + datastore.Key.from_path(kind, int(key_name_or_id)))
                    new_key = datastore.Key.from_path(kind, int(key_name_or_id)) # KEY ID
                    #entity = getattr(model, kind)().get(datastore.Key.from_path(kind, int(key_name_or_id)))
                    entity = getattr(model, kind)().get(new_key)
                    #self.response.out.write("entity? " + str(entity.ID))
                except datastore_errors.EntityNotFoundError: pass
            else:
                
                #print "NAME?"                
                try: 
                    new_key = datastore.Key.from_path(kind, key_name_or_id) # KEY NAME
                    #entity = getattr(model, kind)(datastore.Get(key))
                    entity = getattr(model, kind)().get(new_key)
                except datastore_errors.EntityNotFoundError: pass
            
        if not entity:
            
            
            #if key_name_or_id:               
            #todo: create a new entity - a restore operation!     
            new_key = None
            try:
                if key_name_or_id: 
                    if key_name_or_id[0] in "0123456789":
                        new_key = datastore.Key.from_path(kind, int(key_name_or_id)) # KEY ID
                    else:
                        new_key = datastore.Key.from_path(kind, key_name_or_id) # KEY NAME
            except:
                pass
                            
            if new_key: 
                entity = getattr(model, kind)(key=new_key)  
            else:
                entity = getattr(model, kind)()  
                
                #if key_name_or_id[0] in "0123456789":
                    #newKey = db.Key.from_path(models.Child.kind(), id, parent=p.key())newChild = models.Child(key=newKey, ...)
                    #newKey = db.Key.from_path(models.Child.kind(), id, parent=p.key())
                #    newKey = datastore.Key.from_path(models.Child.kind(), key_name_or_id, parent=p.key())
                    #newChild = models.Child(key=newKey, ...)
                #    return 
                
                #entity = datastore.Entity(kind=kind,name=key_name_or_id)
                
                #entity = model[kind]()     
                #entity = getattr(model, kind)(datastore.Entity(kind=kind,name=key_name_or_id))           
                #entity = getattr(model, kind)(name=key_name_or_id)
                #entity.key = key_name_or_id
            #else:
                #entity = datastore.Entity(kind=kind)
                #entity = getattr(model, kind)(datastore.Entity(kind=kind))
                #entity = getattr(model, kind)()           
        else:
            clear_cache = True
                
        args = self.request.arguments()
        for arg in args:            
            if arg != TYPE_KEY:
                if len(arg) < 100:
                    print arg                
                bar = arg.find('|')
                    
                if bar > 0:
                    field_type = arg[:bar]
                    field_name = str(arg[bar + 1:]).replace("`", "")
                    arg = arg.replace("`", "")
                    value = self.request.get(arg) 
                    if len(value) < 100:
                        print "value?"
                        print value                   
                    if field_type.startswith("*"):
                        field_type = field_type[1]
                        if len(value) == 0:
                            pass
#                            try:
#                                print "trying to delete property in list: " + field_name
#                                if entity.has_key(field_name):
#                                    del entity[field_name]
#                            except Exception, e:
#                                print e
#                                pass
                        else:
                            #entity[field_name] = map(lambda v: rocket_to_ae(field_type, v), value.split('|'))
                            print "list found: " + value
                            
                            #setattr(entity, field_name, value.split('|'))
                            for item in value.split('|'):
                                try:
                                    setattr(entity, field_name, db.Category(item))
                                except:
                                    try:
                                        setattr(entity, field_name, rocket_to_ae(kind, field_name, field_type, item))
                                    except Exception, e:
                                        print "error adding list value to entity in datstore: "
                                        print e
                            
                            #setattr(entity, field_name, map(lambda v: rocket_to_ae(kind, field_name, field_type, v), value.split('|')))
                    else:
                        field_name = field_name.replace("`", "")
                        #if kind == "Weblog":
                        #   self.response.out.write('field_name : ' + field_name)
                        #   self.response.out.write('field_type : ' + field_type)
                        #   self.response.out.write('value : ' + value)
                        #entity[field_name] = rocket_to_ae(field_type, value)
                        try:
                            setattr(entity, field_name, rocket_to_ae(kind, field_name, field_type, value))
                        except Exception, e:
                            print "error setting property: "
                            print e
                        
    
        entity.put()
        #datastore.Put(entity)
        
        if clear_cache:
            # very simplistic measure for ensuring an old version of updated entity is cached
            # assumes that cache key = entity key name
            memcache.delete(key_name_or_id)        
        
        self.response.out.write(u'OK')




def get_type(value):
    if isinstance(value, datetime):
        return TYPE_DATETIME
    elif isinstance(value, bool):
        return TYPE_BOOL
    elif isinstance(value, long):
        return TYPE_LONG
    elif isinstance(value, float):
        return TYPE_FLOAT
    elif isinstance(value, int):
        return TYPE_INT
    elif isinstance(value, datastore_types.Text):
        return TYPE_TEXT
    elif isinstance(value, datastore_types.Key):
        return TYPE_REFERENCE
    elif isinstance(value, datastore_types.Blob):
        return TYPE_BLOB
    else:
        return TYPE_STR
                
    return None



def ae_to_rocket(field_type, ae_value):
    if ae_value == None:
        rocket_value = ""
    elif field_type == TYPE_DATETIME or field_type == TYPE_TIMESTAMP:
        rocket_value = utils.to_iso(ae_value)
    elif field_type == TYPE_REFERENCE:
        rocket_value = "%s/%s" % (ae_value.kind(), ae_to_rocket(TYPE_KEY, ae_value))
    elif field_type == TYPE_KEY:
        #rocket_value = ae_value.name()
        if ae_value.name():
            rocket_value = utils.escape(ae_value.name())
            #rocket_value = ae_value.name()
        else:
            rocket_value = "%d" % ae_value.id()
    elif field_type == TYPE_BOOL:
        rocket_value = "%d" % ae_value
    elif field_type == TYPE_BLOB:
        rocket_value = base64.b64encode(ae_value)
    else:
        #rocket_value = utils.escapeCDATA(u"%s" % ae_value)
        rocket_value = utils.escape(u"%s" % ae_value)
        
    return rocket_value



def rocket_to_ae(kind, field_name, field_type, rocket_value):
    field_type = field_type.lower()
    if not rocket_value:
        ae_value = None
    elif field_type == TYPE_DATETIME or field_type == TYPE_TIMESTAMP:
        ae_value = utils.from_iso(rocket_value)
    elif field_type == TYPE_BOOL:
#        print "!!!"
#        print rocket_value
#        print "!!!"
#        if rocket_value.lower() == "true" or rocket_value == True:
#            ae_value = True
#        elif rocket_value.lower() == "false" or rocket_value == False:
#            ae_value = False
#        else:
        try:
            ae_value = bool(int(rocket_value))
        except:
            ae_value = bool(rocket_value)
    elif field_type == TYPE_LONG:
        ae_value = long(rocket_value)
    elif field_type == TYPE_FLOAT:
        ae_value = float(rocket_value)
    elif field_type == TYPE_INT:
        ae_value = int(rocket_value)
    elif field_type == TYPE_TEXT:
        ae_value = datastore_types.Text(rocket_value.replace('&#124;','|'))
    elif field_type == TYPE_REFERENCE:
        slash = rocket_value.find("/")
        if slash > 0:
            kind = rocket_value[:slash]
            key_name_or_id = rocket_value[slash + 1:]        
            if key_name_or_id[0] in "0123456789":
                key_name_or_id = int(key_name_or_id)
            ae_value = datastore.Key.from_path(kind, key_name_or_id)  
        else:
            logging.error("invalid reference value: %s" % rocket_value)
            ae_value = None
    elif field_type == TYPE_BLOB:
        ae_value = datastore_types.Blob(base64.b64decode(rocket_value))
    else: #str
        test_entity = None
        test_entity_property = None
        try:
            test_entity = getattr(model, kind).all().fetch(1)[0] 
            test_entity_property = getattr(test_entity, field_name) #test_entity[field_name] #
            print "?????????????????????????????"
            print "rocket_to_ae: ?"
            print test_entity
            print field_name
            #test_entity_property = getattr(test_entity, field_name)#().default_value()
            print test_entity_property
            print type(test_entity_property)
            print "?????????????????????????????"
            
        except:
            pass
        
        ae_value = datastore_types.Text(rocket_value.replace('&#124;','|')) #(u"%s" % rocket_value).replace('&#124;','|')
    
    return ae_value
                                                                
             

application = webapp.WSGIApplication([('/rocket/.*', Rocket)], debug=True)

def main():
  run_wsgi_app(application)

if __name__ == "__main__":
  main()